/////////////////////////////////////////////     File     //////////////////////////////////////////////////
// Author: James Chen
// Filename: CUtilities.cpp
// Created: Friday, January 29, 2010 12:13:05 PM
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include ".\CUtilities.h"

//#define	CRITICAL_SECTION_RND		// Not strictly necessary, so off for speed

////////////////////// Singleton Implementation Begins //////////////////////
RNGen *RNGen::m_pInstance = 0;

RNGen::RNGen(void)
{
	srand((unsigned)time(NULL));

	this->SEED = rand();

	MZ = rand();
	MW = rand();

	RN = 0;
}

RNGen::~RNGen(void)
{
}

RNGen *RNGen::GetInstance(void)
{
	if(!m_pInstance)
	{
		m_pInstance = new RNGen;
	}

	return m_pInstance;
}

void RNGen::DeleteInstance(void)
{
	if(m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}

void RNGen::Reseed(void)
{
	this->SEED = rand();

	MZ = rand();
	MW = rand();
}

bool RNGen::LoadLastSeed(void)
{
	ifstream fin;

	fin.open("../data/seeds.txt");
	if(fin.fail())
	{
		return false;
	}

	fin >> this->MW;
	fin >> this->MZ;

	fin.close();

	return true;
}

bool RNGen::SaveSeed(void)
{
	ofstream fout;

	fout.open("../data/seeds.txt");
	if(fout.fail())
	{
		return false;
	}

	fout << this->MW << " " << this->MZ;

	fout.close();

	return true;
}

unsigned int RNGen::FastMW(void)
{
#ifdef		CRITICAL_SECTION_RND
	EnterCriticalSection(&rnd_CS);
#endif

	static int rnd_a = 12345678;
	static int rnd_b = 12393455;
	static int rnd_c = 45432838;

	rnd_a = rnd_a ^ 0x10010100;
	rnd_a = (rnd_a << 1) | ((rnd_a>> 31)&1);
	rnd_a ^= rnd_b ^ rnd_c;
	rnd_b = rnd_b * 255 + 32769;
	rnd_c = rnd_a + rnd_b + rnd_c + 1;

	this->RN = rnd_a;

#ifdef		CRITICAL_SECTION_RND
	LeaveCriticalSection(&rnd_CS);
#endif

	return RN;
}

unsigned int RNGen::FastW(void)
{
#ifdef		CRITICAL_SECTION_RND
	EnterCriticalSection(&rnd_CS);
#endif

	static int m_w = 12393455;    /* must not be zero */
	static int m_z = 45432838;    /* must not be zero */

    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
    m_w = 18000 * (m_w & 65535) + (m_w >> 16);

	this->RN = (m_z << 16) + m_w;

#ifdef		CRITICAL_SECTION_RND
	LeaveCriticalSection(&rnd_CS);
#endif

    return RN;  /* 32-bit result */
}

unsigned int RNGen::FastI(void)
{
	RN = (214013*RN+2531011);
	RN = (RN>>16)&0x7FFF;

    return RN;  /* 32-bit result */
}

unsigned int RNGen::FastSeed(void)
{
	this->MZ = 36969 * (this->MZ & 65535) + (this->MZ >> 16);
    this->MW = 18000 * (this->MW & 65535) + (this->MW >> 16);

	this->RN = (this->MZ << 16) + this->MW;

	return RN;
}

unsigned int RNGen::Slow(void)
{
	RN = rand();
		
	return RN;
}

double RNGen::RandD(RNDTYPE TYPE, double LowValue, double HighValue)
{
	if(LowValue > HighValue)
	{
		double temp;

		temp = HighValue;
		HighValue = LowValue;
		LowValue = temp;
	}

	switch(TYPE)
	{
		case FASTMW:
			{
				this->Ratio = static_cast<double>(this->FastMW())/static_cast<double>(RANDMAX_FASTMW);

				this->ReturnValue = ((HighValue - LowValue)*Ratio)+LowValue;

				return this->ReturnValue;
			}

		case FASTW:
			{
				this->Ratio = static_cast<double>(this->FastW())/static_cast<double>(RANDMAX_FASTW);

				this->ReturnValue = ((HighValue - LowValue)*Ratio)+LowValue;

				return this->ReturnValue;
			}

		case FASTI:
			{
				this->Ratio = static_cast<double>(this->FastI())/static_cast<double>(RANDMAX_FASTI);

				this->ReturnValue = ((HighValue - LowValue)*Ratio)+LowValue;

				return this->ReturnValue;
			}

		case FASTSEED:
			{
				this->Ratio = static_cast<double>(this->FastSeed())/static_cast<double>(RANDMAX_FASTSEED);

				this->ReturnValue = ((HighValue - LowValue)*Ratio)+LowValue;

				return this->ReturnValue;
			}

		case SLOW:
			{
				this->Ratio = static_cast<double>(this->Slow())/static_cast<double>(RANDMAX_SLOW);

				this->ReturnValue = ((HighValue - LowValue)*Ratio)+LowValue;

				return this->ReturnValue;
			}

		default:
			{
				return 0.0;
			}
	}
}

float RNGen::RandF(RNDTYPE TYPE, float LowValue, float HighValue)
{
	if(LowValue > HighValue)
	{
		float temp;

		temp = HighValue;
		HighValue = LowValue;
		LowValue = temp;
	}

	switch(TYPE)
	{
		case FASTMW:
			{
				this->Ratio = static_cast<double>(this->FastMW())/static_cast<double>(RANDMAX_FASTMW);

				this->ReturnValueF = static_cast<float>(((HighValue - LowValue)*Ratio)+LowValue);

				return this->ReturnValueF;
			}

		case FASTW:
			{
				this->Ratio = static_cast<double>(this->FastW())/static_cast<double>(RANDMAX_FASTW);

				this->ReturnValueF = static_cast<float>(((HighValue - LowValue)*Ratio)+LowValue);

				return this->ReturnValueF;
			}

		case FASTI:
			{
				this->Ratio = static_cast<double>(this->FastI())/static_cast<double>(RANDMAX_FASTI);

				this->ReturnValueF = static_cast<float>(((HighValue - LowValue)*Ratio)+LowValue);

				return this->ReturnValueF;
			}

		case FASTSEED:
			{
				this->Ratio = static_cast<double>(this->FastSeed())/static_cast<double>(RANDMAX_FASTSEED);

				this->ReturnValueF = static_cast<float>(((HighValue - LowValue)*Ratio)+LowValue);

				return this->ReturnValueF;
			}

		case SLOW:
			{
				this->Ratio = static_cast<double>(this->Slow())/static_cast<double>(RANDMAX_SLOW);

				this->ReturnValueF = static_cast<float>(((HighValue - LowValue)*Ratio)+LowValue);

				return this->ReturnValueF;
			}

		default:
			{
				return 0.0f;
			}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

CTimer::CTimer(void)
{
	m_nFPS = 0;
    m_dTimeStart = 0.0;
    m_dTimeDelta = 0.0;

	m_dRunTime = 0.0;

	m_bPaused = false;
}

CTimer::~CTimer(void)
{
}

void CTimer::Initialize(void)
{
	LARGE_INTEGER liTicks;

    /* Get processor frequency */
    QueryPerformanceFrequency(&m_liTickFrequency);

    /* Get current tick count */
    QueryPerformanceCounter(&liTicks);

    /* Set the start time */
    m_dTimeStart = double(liTicks.QuadPart) / double(m_liTickFrequency.QuadPart);

    /* Reset the frames per second */
    m_nFPS = 0;
}

void CTimer::Update(void)
{
    static double sfTimePrev = GetTimeSinceStart();
    static double sfTimeLastUpdate = GetTimeSinceStart();
    static int snUpdates = 0;
    double fTimeNow = GetTimeSinceStart();

    /* Increment the frame counter */
    snUpdates++;

    /* Keep track of the frames per second */
    if((fTimeNow - sfTimePrev) > TIMERPOLLRATE)
    {
        m_nFPS = snUpdates / int(fTimeNow - sfTimePrev);
        snUpdates = 0;
        sfTimePrev = fTimeNow;
    }

    /* Calculate the change in time */
	if(!this->m_bPaused)
	{
		m_dTimeDelta = (fTimeNow - sfTimeLastUpdate);
		sfTimeLastUpdate = fTimeNow;
	}

	else
	{
		m_dTimeDelta = 0;
		sfTimeLastUpdate = fTimeNow;
	}

	this->m_dRunTime += m_dTimeDelta;
}

double CTimer::GetTimeSinceStart(void)
{
    LARGE_INTEGER liTicks;
    double fTime;

    /* Get the current tick count */
    QueryPerformanceCounter(&liTicks);

    /* get the current time since the beginning of the computer age */
    fTime = double(liTicks.QuadPart) / double(m_liTickFrequency.QuadPart);

    /* Calculate the time since the last reset */
    return (fTime - m_dTimeStart);
}

double CTimer::GetAverageWorkTime(void)
{
	this->m_dAccumulation = 0;

	for(ITERATOR = this->m_vProfiler.begin(); ITERATOR < this->m_vProfiler.end(); ITERATOR++)
	{
		this->m_dAccumulation += *ITERATOR;
	}

	this->m_dAccumulation /= this->m_vProfiler.size();

	return this->m_dAccumulation;
}

bool CTimer::GetAverageWorkTimeByCount(unsigned int Count, double &average)
{
	if(this->m_vProfiler.size() >= Count)
	{
		this->m_dAccumulation = 0;

		for(ITERATOR = this->m_vProfiler.begin(); ITERATOR < this->m_vProfiler.end(); ITERATOR++)
		{
			this->m_dAccumulation += *ITERATOR;
		}

		this->m_dAccumulation /= this->m_vProfiler.size();

		average = this->m_dAccumulation;

		return true;
	}

	else
	{
		return false;
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////


CMath *CMath::m_pInstance = 0;

CMath::CMath(void)
{
}

CMath::~CMath(void)
{
}

CMath *CMath::GetInstance(void)
{
	if(!m_pInstance)
	{
		m_pInstance = new CMath;
	}

	return m_pInstance;
}

void CMath::DeleteInstance(void)
{
	if(m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}

float CMath::LinearInterpolationF(const float &Min, const float &Max, const float &Progression)
{
	return ((Max-Min)*Progression)+Min;
}
double CMath::LinearInterpolationD(const double &Min, const double &Max, const double &Progression)
{
	return ((Max-Min)*Progression)+Min;
}

D3DXVECTOR3 CMath::Slerp3D(const D3DXVECTOR3 &start, const D3DXVECTOR3 &end, const float &Progression)
{
	this->m_fTheta = acos(D3DXVec3Dot(&start, &end));

	return ((sin((1-Progression)*this->m_fTheta)/sin(this->m_fTheta))*start)+((sin(this->m_fTheta*Progression)/sin(this->m_fTheta))*end);
}

D3DXVECTOR2 CMath::Slerp2D(const D3DXVECTOR2 &start, const D3DXVECTOR2 &end, const float &Progression)
{
	this->m_fTheta = acos(D3DXVec2Dot(&start, &end));

	return ((sin((1-Progression)*this->m_fTheta)/sin(this->m_fTheta))*start)+((sin(this->m_fTheta*Progression)/sin(this->m_fTheta))*end);
}

double CMath::DistanceFast(const D3DXVECTOR3 &start, const D3DXVECTOR3 &end)
{
	return (end.x-start.x)*(end.x-start.x)+(end.y-start.y)*(end.y-start.y)+(end.z-start.z)*(end.z-start.z);
}

double CMath::DistanceTrue(const D3DXVECTOR3 &start, const D3DXVECTOR3 &end)
{
	return sqrt((end.x-start.x)*(end.x-start.x)+(end.y-start.y)*(end.y-start.y)+(end.z-start.z)*(end.z-start.z));
}

double CMath::DistanceFast(const double &xDiff, const double &yDiff, const double &zDiff)
{
	return (xDiff*xDiff)+(yDiff*yDiff)+(zDiff*zDiff);
}

double CMath::DistanceTrue(const double &xDiff, const double &yDiff, const double &zDiff)
{
	return sqrt((xDiff*xDiff)+(yDiff*yDiff)+(zDiff*zDiff));
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////


CSystemAnalysis *CSystemAnalysis::m_pInstance = 0;

CSystemAnalysis::CSystemAnalysis(void)
{
	m_canReadCpu = false;
}

CSystemAnalysis::~CSystemAnalysis(void)
{
	Shutdown();
}

CSystemAnalysis *CSystemAnalysis::GetInstance(void)
{
	if(!m_pInstance)
	{
		m_pInstance = new CSystemAnalysis;
	}

	return m_pInstance;
}

void CSystemAnalysis::DeleteInstance(void)
{
	if(m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}

void CSystemAnalysis::Initialize(void)
{
	// Initialize the flag indicating whether this object can read the system cpu usage or not.
	this->m_canReadCpu = true;

	// Create a query object to poll cpu usage.
	if(PdhOpenQuery(NULL, 0, &m_queryHandle) != ERROR_SUCCESS)
	{
		this->m_canReadCpu = false;
	}

	// Set query object to poll all cpus in the system.
	if(PdhAddCounter(m_queryHandle, TEXT("\\Processor(_Total)\\% processor time"), 0, &m_counterHandle) != ERROR_SUCCESS)
	{
		this->m_canReadCpu = false;
	}

	this->m_ulLastSampleTime = GetTickCount(); 

	this->m_cpuUsage = 0;

	this->memstat.dwLength = sizeof (this->memstat);
}


void CSystemAnalysis::Shutdown(void)
{
	if(m_canReadCpu)
	{
		PdhCloseQuery(this->m_queryHandle);
	}
}


void CSystemAnalysis::Update(void)
{ 
	if(m_canReadCpu)
	{
		if(GetTickCount() > (m_ulLastSampleTime + POLLRATE))
		{
			this->m_ulLastSampleTime = GetTickCount(); 

			PdhCollectQueryData(this->m_queryHandle);
        
			PdhGetFormattedCounterValue(this->m_counterHandle, PDH_FMT_LONG, NULL, &this->m_value);

			this->m_cpuUsage = this->m_value.longValue;
		}
	}

	GlobalMemoryStatusEx(&this->memstat);
}


int CSystemAnalysis::GetCpuUsage(void)
{
	return this->m_canReadCpu ? int(this->m_cpuUsage) : 0;
}

double CSystemAnalysis::GetMemUsage(void)
{
	return double(this->memstat.dwMemoryLoad);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////


CPathing::CPathing(void)
{
	this->m_dSpeed = 0;
	this->m_dTravelDistance = 0;
	this->m_dTotalDistance = 0;

	this->m_bContinuous = false;

	this->m_bLinked = false;
}

CPathing::~CPathing(void)
{
}

void CPathing::RemoveWaypoint(const int &ArrayPosition)
{
	if(static_cast<unsigned int>(ArrayPosition) > this->m_vPath.size())
	{
		return;
	}

	this->m_Iterator = this->m_vPath.begin();

	this->m_Iterator += ArrayPosition;

	this->m_vPath.erase(this->m_Iterator);
}
	
void CPathing::Update(const float &DeltaTime)
{
	this->m_dRemainingSpeed = this->m_dSpeed * DeltaTime;

	//CMath::GetInstance()->DistanceTrue(D3DXVECTOR3(0.0,0.0,0.0), D3DXVECTOR3(1.0,1.0,1.0));

	switch(this->m_nPathType)
	{
	case LINEAR:
		{
			for(this->m_Iterator = m_PositionIterator; this->m_Iterator < (this->m_vPath.end()-1); this->m_Iterator++)
			{
				
			}
		}

	default:
		{

		}
	}
}

void CPathing::AddWaypoints(const D3DXVECTOR3 *Waypoints, const int Count)
{
	static int i=0;

	for(i = 0; i < Count; i++)
	{
		this->m_vPath.push_back(Waypoints[i]);
	}

	if(this->m_vPath.size() == 1)
	{
		this->m_PositionIterator = this->m_vPath.begin();
	}
}

void CPathing::RotateYAxis(const float &angle)
{
	
}

double CPathing::CalculateDistance(void)
{
	static vector<D3DXVECTOR3>::iterator Iterator2;

	this->m_dTotalDistance = 0;

	for(this->m_Iterator = this->m_vPath.begin(); this->m_Iterator < (this->m_vPath.end()-1); this->m_Iterator++)
	{
		Iterator2 = this->m_Iterator+1;
		this->m_dTotalDistance += CMath::GetInstance()->DistanceTrue(Iterator2->x - this->m_Iterator->x,Iterator2->y - this->m_Iterator->y,Iterator2->z - this->m_Iterator->z);
	}

	return this->m_dTotalDistance;
}

void CPathing::Reset(void)
{
	this->m_PositionIterator = this->m_vPath.begin();

	this->m_VecPosition = *this->m_PositionIterator;

	this->m_dTravelDistance = 0;
	this->m_dTotalDistance = 0;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////


CConversion *CConversion::m_pInstance = 0;

CConversion::CConversion(void)
{
}

CConversion::~CConversion(void)
{
}

CConversion *CConversion::GetInstance(void)
{
	if(!m_pInstance)
	{
		m_pInstance = new CConversion;
	}

	return m_pInstance;
}

void CConversion::DeleteInstance(void)
{
	if(m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}

/* Linear Conversions [5/15/2010 1:10 PM by: James Chen] */

double CConversion::FeetToMeter(double value, bool inverse)
{
	return inverse ? value * MetrToFt : value * FtToMetr;
}

double CConversion::YardToMeter(double value, bool inverse)
{
	return inverse ? value * MetrToYard : value * YardToMetr;
}

double CConversion::YardToMile(double value, bool inverse)
{
	return inverse ? value * MiToYard : value * YardToMi;
}

double CConversion::MeterToMile(double value, bool inverse)
{
	return inverse ? value * MiToMetr : value * MetrToMi;
}

double CConversion::KilometerToMile(double value, bool inverse)
{
	return inverse ? value * MiToKM : value * KMToMi;
}

double CConversion::CentimeterToInch(double value, bool inverse)
{
	return inverse ? value * InchToCM : value * CMToInch;
}

/* Weight Conversions [5/15/2010 1:10 PM by: James Chen] */

double CConversion::PoundToKilogram(double value, bool inverse)
{
	return inverse ? value * KgToLb : value * LbToKg;
}

double CConversion::GramToPound(double value, bool inverse)
{
	return inverse ? value * LbToGram : value * GramToLb;
}

double CConversion::GrainToGram(double value, bool inverse)
{
	return inverse ? value * GramToGrn : value * GrnToGram;
}

double CConversion::PoundToGrain(double value, bool inverse)
{
	return inverse ? value * GrnToLb : value * LbToGrn;
}

double CConversion::GrainToKilogram(double value, bool inverse)
{
	return inverse ? value * KgToGrain : value * GrainToKg;
}

/* Speed Conversions [5/15/2010 1:10 PM by: James Chen] */

double CConversion::FeetPerSecToMach(double value, bool inverse)
{
	return inverse ? value * MachToFeetPerSecond : value * FeetPerSecondToMach;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////